{\rtf0\ansi{\fonttbl\f4\fmodern Courier;\f9\ftech Symbol;\f1\fnil Times-Roman;\f0\fswiss Helvetica;\f2\fmodern Ohlfs;}
\margl40
\margr40
{\colortbl;\red0\green0\blue0;}
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f4\b0\i0\ulnone\fs24\fc0\cf0 //ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//
\i 	
\b FILENAME
\b0 :	
\b\i0 eTDocInfo.m\

\b0 //
\i 	
\b SUMMARY
\b0 :	
\b\i0 Required persistent interface for ALL eText documents
\b0 \
//	
\b\i SUPERCLASS
\b0 :
\i0 	
\b Object:HashTable:NXStringTable:eTDocInfo
\b0 \
//	
\b\i PROTOCOLS
\b0 :
\i0 	
\b <ComponentWriting,ComponentReading,InspectableTarget>
\b0 \
//	
\b\i INTERFACE
\b0 :
\i0 	
\b None
\b0 \
//	
\b\i AUTHOR
\b0 :		
\b\i0 Rohit Khare
\b0 \
//	
\b\i COPYRIGHT
\b0 :	
\f9\i0 с
\f4\b 1993,94 California Institure of Technology, eText Project\

\b0 //ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b\i Implementation Comments
\b0\i0 \

\pard\tx520\tx1060\tx1580\tx2120\tx2640\tx3180\tx3720\tx4240\tx4780\tx5300\fc1\cf1 // 		
\i eTDocInfo
\i0 s are descendants of 
\i StringTable
\i0 s,\
//	so they can be extended indefinitely to support dictionary\
//	attributes. Future applications may replace the string table\
//	with a bTree, allowing for more powerful indexing and the\
//	ability to browse eText document collections without loading\
//	all the 
\i .etDocInfo
\i0 s.
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc1\cf1 \

\fc0\cf0 //ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b\i HISTORY
\b0\i0 \
//	12/23/94:	
\b Changed readComponent to cache the "latest" docPath
\b0 \
//	12/23/94:	
\b Overhauled taggingFont system to handle "missing" fonts
\b0 \
//	12/08/94:	
\b Patched Kelsey's HTML generation bugs.
\b0 \
//	10/30/94:	
\b Fixed tag font default registration, added +aliases for tags.
\b0 \
//	10/30/94:	
\b Modified to support <InspectableTarget>
\b0 \
//	10/29/94:	
\b search/regex package rewritten
\i \

\b0\i0 //	10/27/94:	
\b -isVirgin added to public API.
\i \

\b0\i0 //	10/04/94:	
\b -free bug; NXStrTab was free()ing NXAtoms. (was in init/rcfpth)
\i \

\b0\i0 //	10/04/94:	
\b Added eTDoc ivar by analogy to path[] and eTComponents.
\i \

\b0\i0 //	10/03/94:	
\b Added <ComponentWriting>, docInfoTable.
\i \

\b0\i0 //	10/01/94:	
\b Reorganized into categories: eText and eDraw.
\b0 \
//	09/30/94:	
\b Revamped for eText5; renamed eTDocInfo
\b0 \
//	07/20/94:	
\b Added LaTeX support.
\b0 \
//	07/10/94:	
\b Added support for HTML header/trailer writing
\b0 \
//	01/11/94:	
\b Added Agent field.
\b0 \
//	01/11/94:	
\b Added flexible "bindery" methods for extension of navinfo data
\b0 \
//	01/10/94:	
\b Created. See Actors/eTNavinfo
\b0 \
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Imported Interfaces
\b0 \
//\
	#import "
\b eTDocInfo.h
\b0 "\
	#import "
\b eTDocInfoUI.h
\b0 "\
	#import "
\b Kludges
\b0 .subproj/
\b regexpr.h
\b0 "\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b "Class" Variable
\b0 \
//\
	
\b HashTable
\b0  *
\b docInfoTable
\b0 ;\
	\

\i @implementation eTDocInfo\

\i0 //ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
// 	
\b Class Management
\b0 \
//\
+ 
\b initialize
\b0  \{\
	char key[MAXPATHLEN];\
	\
	
\b docInfoTable
\b0  = [[
\b HashTable
\b0  alloc] 
\b initKeyDesc
\b0 :"i"];\
	\

\fc1\cf1 	sprintf(key, "%s%s", 
\b H1
\b0 , TAGNAME);\
	[userModel defaultValue:"
\f1\b\fs60\fc0\cf0 Times-Bold
\f4\b0\fs24\fc1\cf1 " for:key];\
	sprintf(key, "%s%s", 
\b H1
\b0 , TAGSIZE);\
	[userModel defaultValue:"
\b 30.0
\b0 " for:key];\

\fc0\cf0 \

\fc1\cf1 	sprintf(key, "%s%s", 
\b H2
\b0 , TAGNAME);\
	[userModel defaultValue:"
\f1\fs48\fc0\cf0 Times-Roman
\f4\fs24\fc1\cf1 " for:key];\
	sprintf(key, "%s%s", 
\b H2
\b0 , TAGSIZE);\
	[userModel defaultValue:"
\b 24.0
\b0 " for:key];\

\fc0\cf0 \

\fc1\cf1 	sprintf(key, "%s%s", 
\b H3
\b0 , TAGNAME);\
	[userModel defaultValue:"
\f1\b\fs36\fc0\cf0 Times-Bold
\f4\b0\fs24\fc1\cf1 " for:key];\
	sprintf(key, "%s%s", 
\b H3
\b0 , TAGSIZE);\
	[userModel defaultValue:"
\b 18.0
\b0 " for:key];\
	\
	sprintf(key, "%s%s", 
\b H4
\b0 , TAGNAME);\
	[userModel defaultValue:"
\f1\fs36\fc0\cf0 Times-Roman
\f4\fs24\fc1\cf1 " for:key];\
	sprintf(key, "%s%s", 
\b H4
\b0 , TAGSIZE);\
	[userModel defaultValue:"
\b 18.0
\b0 " for:key];\
	\
	sprintf(key, "%s%s", 
\b H5
\b0 , TAGNAME);\
	[userModel defaultValue:"
\f1\b\fs20\fc0\cf0 Times-Bold
\f4\b0\fs24\fc1\cf1 " for:key];\
	sprintf(key, "%s%s", 
\b H5
\b0 , TAGSIZE);\
	[userModel defaultValue:"
\b 10.0
\b0 " for:key];\

\fc0\cf0 \

\fc1\cf1 	sprintf(key, "%s%s", 
\b H6
\b0 , TAGNAME);\
	[userModel defaultValue:"
\f1\fs20\fc0\cf0 Times-Roman
\f4\fs24\fc1\cf1 " for:key];\
	sprintf(key, "%s%s", 
\b H6
\b0 , TAGSIZE);\
	[userModel defaultValue:"
\b 10.0
\b0 " for:key];\

\fc0\cf0 \

\fc1\cf1 	sprintf(key, "%s%s", 
\b QT
\b0 , TAGNAME);\
	[userModel defaultValue:"
\f0\fs28\fc0\cf0 Helvetica
\f4\fs24\fc1\cf1 " for:key];\
	sprintf(key, "%s%s", 
\b QT
\b0 , TAGSIZE);\
	[userModel defaultValue:"
\b 14.0
\b0 " for:key];\

\fc0\cf0 \

\fc1\cf1 	sprintf(key, "%s%s", 
\b BODY
\b0 , TAGNAME);\
	[userModel defaultValue:"
\f1\fs28\fc0\cf0 Times-Roman
\f4\fs24\fc1\cf1 " for:key];\
	sprintf(key, "%s%s", 
\b BODY
\b0 , TAGSIZE);\
	[userModel defaultValue:"
\b 14.0
\b0 " for:key];\

\fc0\cf0 \
	return self;\
\}\
+ (
\b HashTable
\b0  *) 
\b docInfoTable
\b0  \{\
	return docInfoTable;\
\}\
+ 
\b findDocInfo
\b0 :(long) 
\b aDocID
\b0  \{\
	return [
\b docInfoTable
\b0  valueForKey:
\b aDocID
\b0 ]; \
\}\
\
// 
\i Later, twiddling bits 
\b inside
\b0  NXStrTab, I see that it is\

\i0 //
\i 	initialized to %-*, in contravention of the docs.
\i0 \
//\
// 
\i For debugging purposes only: I suspect StrTab's munching me.\

\i0 // 
\b\i Well, NXStringTable IS munching me!!!!!
\b0\i0 \
// 
\i If the current value is == NXUniqueString(aValue), it tries
\i0 \
// 
\i to 
\b free
\b0  the old 
\b NXAtom
\b0 !!!!! So that triggers random lossage\

\i0 // 
\i everywhere... the fix here is evil and expensive, so be it.\

\i0 // 
\i NeXT engineering will burn for this (unless they have some\

\i0 // 
\i extra-special magic on tap in Mecca...)
\i0 \

\pard\tx1140\tx2300\tx3440\tx4600\tx5760\tx6900\tx8060\tx9200\tx10360\tx11520\fc0\cf0 - (void *)insertKey:(const void *)aKey value:(void *)aValue\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 \{\
	NXAtom key,value;\
	\
	key = NXUniqueString(aKey);\
	value = NXUniqueString(aValue);\

\b\fs36 	[self removeKey:key];\

\b0\fs24 	return [super insertKey:key value:value];\
\}\
\
- 
\b init
\b0  \{\
	
\b char
\b0 	
\b idStr
\b0 [9], buf[MAXPATHLEN];\
	struct	
\b passwd
\b0 	*
\b p
\b0 ;\
\
	[super 
\b initKeyDesc
\b0 :"%" 
\b valueDesc
\b0 :"%"];\
	
\b readOnly
\b0  = 
\b NO
\b0 ;\
	
\b virgin
\b0  = 
\b YES
\b0 ;\
	
\b docPath
\b0 [0] = '
\b \\0
\b0 ';\
	
\b etDoc
\b0  = 
\b nil
\b0 ;\
\
	docID = 0;\
	while ((docID == 
\b 0
\b0 ) || ([docInfoTable 
\b isKey
\b0 :
\b docID
\b0 ]))\
		
\b docID
\b0  = [NXApp 
\b uniqueID
\b0 ];\
	[docInfoTable 
\b insertKey
\b0 :docID
\b  value
\b0 :self];\
	
\b sprintf
\b0 (
\b idStr
\b0 ,"
\b %x
\b0 ", 
\b docID
\b0 );\
	[self 
\b insertKey
\b0 :
\b DOCID
\b0  
\b value
\b0 :(char *)NXUniqueString(
\b idStr
\b0 )];\
	[self 
\b setDocTitle
\b0 :
\b "Untitled Document"
\b0 ];\
	[self 
\b setType
\b0 :
\b eTEXT
\b0 ];		//
\i eTextDoc type extension
\i0 \
	[self 
\b initTagFields
\b0 ];		//
\i eTextDoc init extension
\i0 \
	p = 
\b getpwuid
\b0 (
\b getuid
\b0 ());\
	[self 
\b setAuthor
\b0 :p->pw_gecos ? 
\b  p->pw_gecos
\b0  : "
\b Author's Name
\b0 "];\
	[self 
\b setAgent
\b0 :""];\
	[self 
\b setParent
\b0 :""];\
	[self 
\b setPeers
\b0 :""];\
	[self 
\b setKeywords
\b0 :""];\
	sprintf(buf, "\{\\\\rtf0\\\\ansi\{\\\\fonttbl\\\\f1\\\\fnil Times-Roman;\\\\f2\\\\fswiss Helvetica;\}\\n\\\\margl40\\n\\\\margr40\\n\\\\pard\\\\tx520\\\\tx1060\\\\tx1600\\\\tx2120\\\\tx2660\\\\tx3200\\\\tx3720\\\\tx4260\\\\tx4800\\\\tx5320\\\\f1\\\\b0\\\\ulnone\\\\fs24\\\\fc0\\\\cf0 Rich-Text \\n\\\\b comments\\n\\\\b0  about this document\\n
\b --%s
\b0 \\n\}\\n", [NXApp 
\b date
\b0 ]);\
	[self 
\b setRTFComments
\b0 :buf]; // 
\f1 Rich Text 
\i comments
\i0  about this document
\f4 \
	[self 
\b insertKey
\b0 :
\b MDFYDATE
\b0  
\b value
\b0 :NXUniqueString([NXApp 
\b date
\b0 ])]; \
	return self;\
\}\
- 
\b free
\b0  \{\
	// 
\i If another docInfo is already registered with its key, 
\b OR
\b0\i0 \
	// 
\i If this docInfo is a virgin (no saves)
\i0 \
	// 
\i Then (remove it from the table) free it\
	\
	
\i0 if (
\b virgin
\b0  && ([
\b docInfoTable
\b0  valueForKey:
\b docID
\b0 ] == 
\b self
\b0 )) \{\
		[docInfoTable 
\b removeKey
\b0 :
\b docID
\b0 ];\
	\}\
	if (
\b virgin
\b0  || ([
\b docInfoTable
\b0  valueForKey:
\b docID
\b0 ] != 
\b self
\b0 ))\{\
		[self 
\b empty
\b0 ]; // 
\i Memory leak, but these are reusable NXAtoms anyway
\i0 \
		return self = [super 
\b free
\b0 ];\
	\}\
	return 
\b self
\b0 ;\
\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b <ComponentReading, ComponentWriting>
\b0 \
//\
- 
\b writeComponentToPath
\b0 :(NXAtom)path 
\b inFormat
\b0 :(int) theFormat \{\
	char	
\b docInfoPath
\b0 [MAXPATHLEN];\
	\
	sprintf(
\b docInfoPath
\b0 , "
\b %s/%s
\b0 ", path, 
\b DOCINFOFILE
\b0 );\
	[self 
\b insertKey
\b0 :
\b MDFYDATE
\b0  
\b value
\b0 :NXUniqueString([NXApp 
\b date
\b0 ])]; \
	[self 
\b writeToFile
\b0 :docInfoPath];\
	[
\b etDoc
\b0  
\b registerComponent
\b0 :
\b DOCINFOFILE
\b0 ];\
	
\b virgin
\b0  = 
\b NO
\b0 ;\
	return self;\
\}\
- 
\b readComponentFromPath
\b0 :(NXAtom)path \{\
	char	
\b docInfoPath
\b0 [MAXPATHLEN]; id aDI;\
	\
	if ([docInfoTable 
\b valueForKey
\b0 :
\b docID
\b0 ] == 
\b self
\b0 )\
		[docInfoTable 
\b removeKey
\b0 :
\b docID
\b0 ]; // 
\i init: registered the untitled ID
\i0 \
	strncpy(
\b docPath
\b0 , 
\b path
\b0 , MAXPATHLEN);\
\
	// 
\i OK, we have an etfd path, try concatenating DOCINFOFILE
\i0 .\
	sprintf(
\b docInfoPath
\b0 , "%s/%s", 
\b docPath
\b0 , 
\b DOCINFOFILE
\b0 );\
	if (
\b access
\b0 (docInfoPath, R_OK | F_OK)) \{\
		// 
\i backward-compatibility with PR1 documents
\i0 .\
		sprintf(
\b docInfoPath
\b0 , "%s/%s", 
\b docPath
\b0 , 
\b NAVINFOFILE1
\b0 );\
		if (
\b access
\b0 (docInfoPath, R_OK | F_OK)) \{\
			// 
\i backward-compatibility with PR2 documents
\i0 .\
			sprintf(
\b docInfoPath
\b0 , "%s/%s", 
\b docPath
\b0 , 
\b NAVINFOFILE2
\b0 );\
			if (
\b access
\b0 (docInfoPath, R_OK | F_OK)) \{\
				// 
\i Last-Ditch check: is path itself the file? [etApp openFile]
\i0 \
				strncpy(
\b docInfoPath
\b0 , 
\b path
\b0 , MAXPATHLEN);\
				strncpy(
\b docPath
\b0 , 
\b docInfoPath
\b0 , MAXPATHLEN);\
				*
\b rindex
\b0 (
\b docPath
\b0 , '
\b /
\b0 ') = '
\b \\0
\b0 ';\
			\}\
		\}\
	\}\
	if (
\b access
\b0 (docInfoPath, R_OK | F_OK)) \{\
		[NXApp 
\b delayedFree
\b0 :self];\
		return nil;\
	\}\
	if (
\b !
\b0 [self 
\b readFromFile
\b0 :docInfoPath]) \{\
		[NXApp 
\b delayedFree
\b0 :self];\
		return nil;\
	\}\
	// 
\i Quick Patch for pre-5.0 documents\
	
\i0 if  ([self 
\b isKey
\b0 :"
\b title
\b0 "] && \
		([self 
\b docTitle
\b0 ] == NXUniqueString("
\b Untitled Document
\b0 ")))\
		[self 
\b setDocTitle
\b0 :[self 
\b valueForStringKey
\b0 :"
\b title
\b0 "]];\
	
\b sscanf
\b0 ([self valueForStringKey:
\b DOCID
\b0 ], "
\b %x
\b0 ", 
\b &docID
\b0 );\
	aDI = [
\b docInfoTable
\b0  valueForKey:
\b docID
\b0 ];\
	if (!aDI)\{\
		[docInfoTable 
\b insertKey
\b0 :docID
\b  value
\b0 :self];\
	\} else if (aDI != self)\{\
		id 
\b realInfo
\b0 ;\
		\
		[NXApp 
\b delayedFree
\b0 :self];\
		
\b realInfo
\b0  = [
\b docInfoTable
\b0  valueForKey:
\b docID
\b0 ];\
		[realInfo 
\b setDocPath
\b0 :path];\
		return 
\b realInfo
\b0 ;\
	\}\
	
\b virgin
\b0  = 
\b NO
\b0 ;\
	return self;\
\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Accessor Methods
\b0 \
//\
- 
\b setDoc
\b0 : newDoc
\b  
\b0 \{\
	// 
\i What about (un)registerNotification: ?
\i0 \
	
\b etDoc
\b0  = 
\b newDoc
\b0 ;\
	if (
\b etDoc
\b0 ) [etDoc 
\b registerNotification
\b0 :self];\
	return self;\
\}	\
- 
\b etDoc 
\b0 \{\
	return 
\b etDoc
\b0 ;\
\}\
\
- (
\b long
\b0 ) 
\b docID 
\b0 \{\
	return 
\b docID
\b0 ;\
\}	\
- (NXAtom) 
\b docIDStr 
\b0 \{\
	return [self 
\b valueForStringKey
\b0 :
\b DOCID
\b0 ];\
\}\
\
- (NXAtom) 
\b docTitle 
\b0 \{\
	return [self 
\b valueForStringKey
\b0 :
\b DOCTITLE
\b0 ];\
\}\
- 
\b setDocTitle
\b0 :(const char *) newTitle \{\
	if (index(newTitle, '/')) \{\
		char patched[MAXPATHLEN];\
		char *i;\
		\
		strcpy(patched, newTitle);\
		for (i = patched; *i; i++)\
			if (*i = '/') *i = '_';\
		[self 
\b insertKey
\b0 :
\b DOCTITLE
\b0  
\b value
\b0 :NXUniqueString(
\b patched
\b0 )];\
	\} else \{\
		[self 
\b insertKey
\b0 :
\b DOCTITLE
\b0  
\b value
\b0 :NXUniqueString(
\b newTitle
\b0 )];\
	\}\
	return self;\
\}\
\
- (NXAtom) 
\b docType 
\b0 \{\
	return [self 
\b valueForStringKey
\b0 :
\b DOCTYPE
\b0 ];\
\}\
- 
\b setType
\b0 :(const char *) newType \{\
	[self 
\b insertKey
\b0 :
\b DOCTYPE
\b0  
\b value
\b0 :NXUniqueString(
\b newType
\b0 )]; \
	return self;\
\}\
\
- (NXAtom) 
\b author 
\b0 \{\
	return [self 
\b valueForStringKey
\b0 :
\b AUTHOR
\b0 ];\
\}\
- 
\b setAuthor
\b0 :(const char *) newAuthor \{\
	[self 
\b insertKey
\b0 :
\b AUTHOR
\b0  
\b value
\b0 :NXUniqueString(
\b newAuthor
\b0 )]; \
	return self;\
\}\
\
- (NXAtom) 
\b agent 
\b0 \{\
	return [self 
\b valueForStringKey
\b0 :
\b AGENT
\b0 ];\
\}\
- 
\b setAgent
\b0 :(const char *) newAgent \{\
	[self 
\b insertKey
\b0 :
\b AGENT
\b0  
\b value
\b0 :NXUniqueString(
\b newAgent
\b0 )]; \
	return self;\
\}\
\
- (
\b long
\b0 ) 
\b parentID
\b0  \{\
	long 
\b pID
\b0 ; int success;\
	\
	success = 
\b sscanf
\b0 ([self 
\b parent
\b0 ],"
\b %x
\b0 ", 
\b &pID
\b0 );\
	
\b return
\b0  (success ? 
\b pID
\b0  : 
\b 0
\b0 );\
\}\
- (NXAtom) 
\b parent 
\b0 \{\
	return [self 
\b valueForStringKey
\b0 :
\b PARENT
\b0 ];\
\}\
- 
\b setParent
\b0 :(const char *) newParent \{\
	[self 
\b insertKey
\b0 :
\b PARENT
\b0  
\b value
\b0 :NXUniqueString(
\b newParent
\b0 )]; \
	return self;\
\}\
- 
\b setParentID
\b0 :(
\b long
\b0 ) newParentID \{\
	char newParent[64];\
	\
	
\b sprintf
\b0 (newParent, "
\b %x
\b0 ", 
\b newParentID
\b0 );\
	[self 
\b insertKey
\b0 :
\b PARENT
\b0  
\b value
\b0 :NXUniqueString(
\b newParent
\b0 )]; \
	return self;\
\}\
\
- (NXAtom) 
\b peers
\b0  \{\
	return [self 
\b valueForStringKey
\b0 :
\b PEERS
\b0 ];\
\}\
- 
\b setPeers
\b0 :(const char *) newPeers \{\
	[self 
\b insertKey
\b0 :
\b PEERS
\b0  
\b value
\b0 :NXUniqueString(
\b newPeers
\b0 )]; \
	return self;\
\}\
- 
\b addPeerID
\b0 :(
\b long
\b0 ) newPeerID \{\
	char newPeers[MAXPATHLEN]; 		// 
\i Long enough? C Programmer's Disease.
\i0 \
	\
	
\b strncpy
\b0 (
\b newPeers
\b0 , [self 
\b peers
\b0 ], MAXPATHLEN);\
	
\b sprintf
\b0 (newPeers, " 
\b %x
\b0 ", 
\b newPeerID
\b0 );\
	[self 
\b insertKey
\b0 :
\b PEERS
\b0  
\b value
\b0 :NXUniqueString(
\b newPeers
\b0 )]; \
	return self;\
\}\
\
- (NXAtom) 
\b keywords
\b0  \{\
	return [self 
\b valueForStringKey
\b0 :
\b KEYWORDS
\b0 ];\
\}\
- 
\b setKeywords
\b0 :(const char *) newKeywords \{\
	[self 
\b insertKey
\b0 :
\b KEYWORDS
\b0  
\b value
\b0 :NXUniqueString(
\b newKeywords
\b0 )]; \
	return self;\
\}\
\
- (NXAtom) 
\b rtfComments
\b0  \{		//
\i  RTF source of comment field\

\i0 	return [self 
\b valueForStringKey
\b0 :
\b COMMENTS
\b0 ];\
\}\
- 
\b setRTFComments
\b0 :(const char *) newComments \{\
	[self 
\b insertKey
\b0 :
\b COMMENTS
\b0  
\b value
\b0 :NXUniqueString(
\b newComments
\b0 )]; \
	return self;\
\}\
- (const char *) 
\b comments
\b0  \{	// 
\i ASCII contents of comment field\

\i0 	
\b static
\b0  char 	
\b comments
\b0 [MAXPATHLEN];\
	NXStream 		*
\b stream
\b0 ;\
	id 				
\b theText
\b0  = [NXApp 
\b sharedText
\b0 ];\
	const char 		*
\b rtfSrc
\b0 ;\

\i 	\

\i0 	
\b rtfSrc
\b0  = [self 
\b rtfComments
\b0 ];\
	
\b stream
\b0  = 
\b NXOpenMemory
\b0 (
\b rtfSrc
\b0 ,
\b strlen
\b0 (rtfSrc), 
\b NX_READONLY
\b0 );\
	[
\b theText
\b0  
\b readRichText
\b0 :stream];\
	
\b NXClose
\b0 (stream);\
	[
\b theText
\b0  
\b getSubstring
\b0 :
\b comments
\b0  start:0 length:MAXPATHLEN-1];\
	
\b comments
\b0 [MAXPATHLEN-1]=0;\
	
\b return
\b0  
\b comments
\b0 ;\
\}\
\
- (NXAtom) 
\b lastModifyDate
\b0  \{\
	return [self 
\b valueForStringKey
\b0 :
\b MDFYDATE
\b0 ];\
\}\
\
- (NXAtom) 
\b valueOf
\b0 : (NXAtom) 
\b theQuery
\b0  \{\
	
\b NXAtom
\b0  tmp = [self 
\b valueForStringKey
\b0 :
\b theQuery
\b0 ];\
	
\b return
\b0  (tmp ? 
\b tmp
\b0  : NXUniqueString
\b ("")
\b0 );\
\}\
- 
\b bindKey
\b0 :(const char *) 
\b newKey
\b0  
\b to
\b0 : (const char *) 
\b newVal
\b0  \{\
	[self 
\b insertKey
\b0 :NXUniqueString(
\b newKey
\b0 ) 
\b value
\b0 :NXUniqueString(
\b newVal
\b0 )];\
	return self;\
\}\
\
//ппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Runtime Properties
\b0 \
//\
- (const char *) 
\b docPath
\b0  \{\
	return 
\b docPath
\b0 ;\
\}	\
- 
\b setDocPath
\b0 :(const char *) newPath\
\{\
	
\b if 
\b0 (!strncmp([self 
\b docTitle
\b0 ], "
\b Untitled
\b0 ", 8))\{ // 
\i Matches "Untitled*"\

\i0 		char 
\b tmp
\b0 [MAXPATHLEN];			             // 
\i C Programmer's Disease
\i0 \
		
\b strncpy
\b0 (
\b tmp
\b0 , 
\b rindex
\b0 (
\b newPath
\b0 , '
\b /
\b0 ')+1, MAXPATHLEN);\
		if (rindex(tmp, '.')) *rindex(tmp, '.')= 0; \
		[self 
\b setDocTitle
\b0 :
\b tmp
\b0 ];\
	\}\
	
\b strncpy
\b0 (
\b docPath
\b0 , 
\b newPath
\b0 , MAXPATHLEN); \
	// 
\i how do we tell eTDocInfoUI its info has been invalidated?\
	
\i0 if ([
\b inspector
\b0  
\b currentObj
\b0 ] == self) \{\
		[
\b inspector
\b0  
\b refresh
\b0 ];\
	\}\
	return self;\
\}\
\
- (BOOL) 
\b readOnly
\b0  \{\
	if (
\b !access
\b0 (
\b docPath
\b0 , 
\b F_OK
\b0 ))\
		return 
\b readOnly
\b0  
\b |
\b0 = (BOOL) 
\b access
\b0 (
\b docPath
\b0 , 
\b W_OK
\b0 );\
	return 
\b readOnly
\b0 ;\
\}\
- 
\b setReadOnly
\b0 :(BOOL) newState \{\
	if (
\b !access
\b0 (
\b docPath
\b0 , 
\b F_OK
\b0 ))\
		
\b readOnly
\b0  = newState | 
\b access
\b0 (
\b docPath
\b0 , 
\b W_OK
\b0 );\
	else\
		
\b readOnly
\b0  = newState;\
	return self;\
\}\
\
- (BOOL) 
\b isVirgin
\b0  \{\
	return 
\b virgin
\b0 ;\}\
- (id <
\b Inspectable
\b0 >) 
\b inspectableDelegate
\b0  \{\
	return [[
\b eTDocInfoUI
\b0  new] 
\b setDocInfo
\b0 :
\b self
\b0 ]; \}\

\pard\tx1140\tx2300\tx3440\tx4600\tx5760\tx6900\tx8060\tx9200\tx10360\tx11520\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 //пппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Query/Regex Matching
\b0 \
//\
- (
\b BOOL
\b0 ) 
\b searchFor
\b0 :(NXAtom) 
\b regex
\b0  
\b in
\b0 :(NXAtom) 
\b field
\b0  \{\
	BOOL 
\b found
\b0  = 
\b NO
\b0 ; char *err;\
	BOOL di, pa, dti, dty, au, ag, pe, kw, co;       // 
\i field masks
\i0 \
 	\
	if (!field || (*field == '\\0') || !regex) return 
\b NO
\b0 ; // 
\i error case
\i0 \
	if (!strcmp(
\b regex
\b0 ,
\b ANYFIELD
\b0 )) return 
\b YES
\b0 ;  // 
\i special case, "*" in any field
\i0 \
	if (err = ylo_
\b re_comp
\b0 (
\b field
\b0 ))		              // 
\i re_comp is static\

\i0 		\{NXLogError("FieldSpec: %s",err); return 
\b NO
\b0 ;\}\
	
\b di
\b0  = ylo_re_exec(
\b DOCID
\b0 );\
	
\b pa
\b0  = ylo_re_exec(
\b PARENT
\b0 );\
	
\b dti
\b0 = ylo_re_exec(
\b DOCTITLE
\b0 );\
	
\b dty
\b0 = ylo_re_exec(
\b DOCTYPE
\b0 );\
	
\b au
\b0  = ylo_re_exec(
\b AUTHOR
\b0 );\
	
\b ag 
\b0 = ylo_re_exec(
\b AGENT
\b0 );\
	
\b pe
\b0  = ylo_re_exec(
\b PEERS
\b0 );\
	
\b kw
\b0  = ylo_re_exec(
\b KEYWORDS
\b0 );\
	
\b co
\b0  = ylo_re_exec(
\b COMMENTS
\b0 );\
	if (regex && (*regex == 0)) ylo_re_comp("^$");   // 
\i rule 11, man ed(1)
\i0 \
	else if (err = ylo_
\b re_comp
\b0 (
\b regex
\b0 ))               // 
\i re_comp is static\

\i0 		\{NXLogError("QuerySpec: %s",err); return 
\b NO
\b0 ;\}\
	if (!found && 
\b di
\b0 ) found |= ylo_re_exec([self 
\b docIDStr
\b0 ]);\
	if (!found && 
\b pa
\b0 ) found |= ylo_re_exec([self 
\b parent
\b0 ]);\
	if (!found && 
\b dti
\b0 )found |= ylo_re_exec([self 
\b docTitle
\b0 ]);\
	if (!found && 
\b dty
\b0 )found |= ylo_re_exec([self 
\b docType
\b0 ]);\
	if (!found && 
\b au
\b0 ) found |= ylo_re_exec([self 
\b author
\b0 ]);\
	if (!found && 
\b ag
\b0 ) found |= ylo_re_exec([self 
\b agent
\b0 ]);\
	if (!found && 
\b pe
\b0 ) found |= ylo_re_exec([self 
\b peers
\b0 ]);\
	if (!found && 
\b kw
\b0 ) found |= ylo_re_exec([self 
\b keywords
\b0 ]);\
		// 
\i Note that we search the RTF-coded comments field
\i0 \
		// 
\i -- not the ascii-filtered version
\i0 \
	if (!found && 
\b co
\b0 ) found |= ylo_re_exec([self 
\b rtfComments
\b0 ]);\
	
\b return found
\b0 ;\
\}\
\

\i @end\

\pard\tx1140\tx2300\tx3440\tx4600\tx5760\tx6900\tx8060\tx9200\tx10360\tx11520\fc0\cf0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\i0\fc0\cf0 //пппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Text-specific Extensions
\b0 \
//\

\i @implementation eTDocInfo(eText)
\i0 \
//пппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Generating Indexes
\b0 \
//\
- 
\b setEmitTrailer
\b0 :(
\b BOOL
\b0 )newState \{\
	[self insertKey:
\b TRAILER
\b0  value:
\b (newState ? "YES" : "NO")
\b0 ]; \
	return self;\
\}\
- (
\b BOOL
\b0 )
\b emitTrailer
\b0  \{\
	NXAtom defVal = [self valueForStringKey:
\b TRAILER
\b0 ];\
	if (!defVal || !strcmp(defVal, "
\b YES
\b0 ")) return 
\b YES
\b0 ;\
	return 
\b NO
\b0 ;\}\
\
//пппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Tagging Fonts
\b0 \
//\
- 
\b initTagFields
\b0  \{\
	int i;\
	NXAtom tags[8] = \{H1, H2, H3, H4, H5, H6, QT, BODY\};\
	for(i=0; i<8; i++)\
		[self 
\b setTag
\b0 :tags[i] to:[self 
\b tagFont
\b0 :tags[i]]];\
	[self 
\b setEmitTrailer
\b0 :
\b YES
\b0 ];\
	return self;\
\}\
- 
\b setTag
\b0 :(const char *)tagType 
\b to
\b0 : (
\b Font
\b0  *)newFont \{\
	char 	
\b tagName
\b0 [MAXPATHLEN];\
	char 	
\b tagSize
\b0 [MAXPATHLEN];\
	char 	
\b fontSize
\b0 [MAXPATHLEN];\
	\
	sprintf(
\b tagName
\b0 , "
\b %s%s
\b0 ", 
\b tagType
\b0 , 
\b TAGNAME
\b0 );\
	sprintf(
\b tagSize
\b0 , "
\b %s%s
\b0 ", 
\b tagType
\b0 , 
\b TAGSIZE
\b0 );\
	sprintf(
\b fontSize
\b0 , "
\b %f
\b0 ", [newFont 
\b pointSize
\b0 ]);\
	[self 
\b bindKey
\b0 :NXUniqueString(tagName) 
\b to
\b0 :NXUniqueString([
\b newFont
\b0  
\b name
\b0 ])];\
	[self 
\b bindKey
\b0 :NXUniqueString(tagSize) 
\b to
\b0 :NXUniqueString(
\b fontSize
\b0 )];\
	return self;\
\}\
// 
\i The following table is used to cache errors in 
\b tagFont
\b0  and 
\b defaultTagFont
\b0\i0 .\
static id	
\b errTab
\b0  = nil;\
\
- 
\b tagFont
\b0 :(const char *)tagType \{\
	char 		
\b tagName
\b0 [MAXPATHLEN];\
	char 		
\b tagSize
\b0 [MAXPATHLEN];\
	const char 	*
\b fontName
\b0 ;\
	const char	*
\b fontSizeStr
\b0 ;\
	float		
\b fontSize
\b0 ;\
	id			
\b theFont
\b0 ;\
	\
	sprintf(
\b tagName
\b0 , "
\b %s%s
\b0 ", 
\b tagType
\b0 , 
\b TAGNAME
\b0 );\
	sprintf(
\b tagSize
\b0 , "
\b %s%s
\b0 ", 
\b tagType
\b0 , 
\b TAGSIZE
\b0 );\
	
\b fontName
\b0  = [self 
\b valueOf
\b0 :
\b tagName
\b0 ];\
	
\b fontSizeStr
\b0  = [self 
\b valueOf
\b0 :
\b tagSize
\b0 ];\
	if (!(*fontName && *fontSizeStr)) \{\
		fontName = [
\b userModel
\b0  stringQuery:
\b tagName
\b0 ];\
		fontSizeStr = [
\b userModel
\b0  stringQuery:
\b tagSize
\b0 ];\
	\}\
	
\b sscanf
\b0 (fontSizeStr,"%f",&
\b fontSize
\b0 );\
	
\b theFont
\b0  = [Font 
\b newFont
\b0 :
\b fontName
\b0  
\b size
\b0 :
\b fontSize
\b0  
\b matrix
\b0 :
\b NX_FLIPPEDMATRIX
\b0 ];\
	if (
\b !theFont
\b0 ) \{\
		if (!errTab) \{errTab = [[HashTable alloc] initKeyDesc:"%"];\}\
		if (![errTab isKey:fontName])\{\

\b 			NXLogError
\b0 ("Can't find %s font for the %s doc tag!",
\b fontName
\b0 ,
\b tagType
\b0 );\
			[errTab insertKey:fontName value:nil];\
		\}\
		// 
\i Can we figure out if the fonts were supposed to be italic, bold, etc?
\i0 \
		
\b theFont
\b0  =[Font 
\b newFont
\b0 :
\b "Courier"
\b0  
\b size
\b0 :
\b fontSize
\b0  
\b matrix
\b0 :
\b NX_FLIPPEDMATRIX
\b0 ];\
	\}\
	return 
\b theFont;
\b0 \
\}\
- 
\b registerTag
\b0 :(const char *)tagType 
\b to
\b0 : (
\b Font
\b0  *)newFont \{\
	return [eTDocInfo 
\b registerTag
\b0 :tagType 
\b to
\b0 :newFont];\}\
+ 
\b registerTag
\b0 :(const char *)tagType 
\b to
\b0 : (
\b Font
\b0  *)newFont \{\
	char 	
\b tagName
\b0 [MAXPATHLEN];\
	char 	
\b tagSize
\b0 [MAXPATHLEN];\
	char 	
\b fontSize
\b0 [MAXPATHLEN];\
	\
	sprintf(
\b tagName
\b0 , "
\b %s%s
\b0 ", 
\b tagType
\b0 , 
\b TAGNAME
\b0 );\
	sprintf(
\b tagSize
\b0 , "
\b %s%s
\b0 ", 
\b tagType
\b0 , 
\b TAGSIZE
\b0 );\
	sprintf(
\b fontSize
\b0 , "
\b %f
\b0 ", [newFont 
\b pointSize
\b0 ]);\
	[
\b userModel
\b0  
\b bindKey
\b0 :tagName 
\b to
\b0 :[
\b newFont
\b0  
\b name
\b0 ]];\
	[
\b userModel
\b0  
\b bindKey
\b0 :tagSize 
\b to
\b0 :
\b fontSize
\b0 ];\
	return self;\
\}\
- 
\b defaultTagFont
\b0 :(const char *)tagType \{\
	return [eTDocInfo 
\b defaultTagFont
\b0 :tagType];\}\
+ 
\b defaultTagFont
\b0 :(const char *)tagType \{\
	char 		
\b tagName
\b0 [MAXPATHLEN];\
	char 		
\b tagSize
\b0 [MAXPATHLEN];\
	const char 	*
\b fontName
\b0 ;\
	const char	*
\b fontSizeStr
\b0 ;\
	float		
\b fontSize
\b0 ;\
	id			
\b theFont
\b0 ;\
	\
	\
	sprintf(
\b tagName
\b0 , "
\b %s%s
\b0 ", 
\b tagType
\b0 , 
\b TAGNAME
\b0 );\
	sprintf(
\b tagSize
\b0 , "
\b %s%s
\b0 ", 
\b tagType
\b0 , 
\b TAGSIZE
\b0 );\
	fontName = [
\b userModel
\b0  stringQuery:
\b tagName
\b0 ];\
	fontSizeStr = [
\b userModel
\b0  stringQuery:
\b tagSize
\b0 ];\
	
\b sscanf
\b0 (fontSizeStr,"%f",&
\b fontSize
\b0 );\
	
\b theFont
\b0  = [Font 
\b newFont
\b0 :
\b fontName
\b0  
\b size
\b0 :
\b fontSize
\b0  
\b matrix
\b0 :
\b NX_FLIPPEDMATRIX
\b0 ];\
	if (
\b !theFont
\b0 ) \{\
		if (!errTab) \{errTab = [[HashTable alloc] initKeyDesc:"%"];\}\
		if (![errTab isKey:fontName])\{\

\b 			NXLogError
\b0 ("Can't find %s font for the %s default!",
\b fontName
\b0 ,
\b tagType
\b0 );\
			[errTab insertKey:fontName value:nil];\
		\}\
		// 
\i Can we figure out if the fonts were supposed to be italic, bold, etc?
\i0 \
		
\b theFont
\b0  =[Font 
\b newFont
\b0 :
\b "Courier"
\b0  
\b size
\b0 :
\b fontSize
\b0  
\b matrix
\b0 :
\b NX_FLIPPEDMATRIX
\b0 ];\
	\}\
	return 
\b theFont;
\b0 \
\}\
\
//пппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Alternate Formats
\b0 \
//\
- 
\b writeHTMLHeader
\b0 :(
\b NXStream
\b0  *)s	// 
\i required
\i0 \
\{\
	char tmp[MAXPATHLEN];\
	long aDocID;\
	id	 theDocInfo;\
	id   docInfoList;\
	NXAtom peers;\
	int	i;\
\
	NXPrintf(s,"<
\b HEAD
\b0 >\\n<
\b TITLE
\b0 >%
\b v
\b0 </
\b TITLE
\b0 >\\n",[self 
\b docTitle
\b0 ]);\
	\
	strcpy(
\b tmp
\b0 , [self 
\b parent
\b0 ]);\
	
\b sscanf
\b0 (tmp, "%x", &
\b aDocID
\b0 );\
	
\b theDocInfo
\b0  = [
\b docInfoTable
\b0  valueForKey:
\b aDocID
\b0 ];\
	if (
\b theDocInfo
\b0 ) \{\
		strcpy(
\b tmp
\b0 , [theDocInfo 
\b docPath
\b0 ]);\
		*rindex(tmp,'
\b .
\b0 ')=
\b 0
\b0 ;\
		NXPrintf(s,"<
\b LINK HREF
\b0 =\\"../%
\b V.
\b0 "
\b\fc1\cf1 HTMD_EXT
\b0\fc0\cf0 "
\b /"HTML_INDEX"
\b0 \\" 
\b REL
\b0 =\\"
\b Parent
\b0 \\" 
\b TITLE
\b0 =\\"%
\b v
\b0 \\">\\n", 
\b rindex
\b0 (tmp,'
\b /
\b0 ')+1,[theDocInfo 
\b docTitle
\b0 ]);\
	\}\
	\
	docInfoList = [[NXApp 
\b navigator
\b0 ] 
\b query
\b0 :[self 
\b docIDStr
\b0 ] 
\b field
\b0 :
\b PARENT
\b0 ];\
	i = [docInfoList count];\
	for (;i;i--) \{\
		strcpy(
\b tmp
\b0 , [[
\b docInfoList
\b0  objectAt:(i-1)] 
\b docPath
\b0 ]);\
		*rindex(tmp,'.')=0;\
		NXPrintf(s,"<
\b LINK HREF
\b0 =\\"../
\b %V.
\b0 "
\b\fc1\cf1 HTMD_EXT
\b0\fc0\cf0 "
\b /"HTML_INDEX"
\b0 \\" REL=\\"
\b Child
\b0 \\" 
\b TITLE
\b0 =\\"%
\b v
\b0 \\">\\n", rindex(tmp,'/')+1,[[
\b docInfoList
\b0  objectAt:(i-1)] 
\b docTitle]
\b0 );\
	\}\
\
//	if ([self 
\b parent
\b0 ] != 
\b NXUniqueString
\b0 (
\b ""
\b0 )) \{\
		docInfoList = [[NXApp 
\b navigator
\b0 ] 
\b query
\b0 :[self 
\b parent
\b0 ] 
\b field
\b0 :
\b PARENT
\b0 ];\
		i = [docInfoList count];\
		for (;i;i--) \{\
			if ([
\b docInfoList
\b0  objectAt:(i-1)] != 
\b self
\b0 ) \{\
				strcpy(
\b tmp
\b0 , [[
\b docInfoList
\b0  objectAt:(i-1)] 
\b docPath
\b0 ]);\
				*rindex(tmp,'.')=0;\
				NXPrintf(s,"<
\b LINK HREF
\b0 =\\"../
\b %V.
\b0 "
\b\fc1\cf1 HTMD_EXT
\b0\fc0\cf0 "
\b /"HTML_INDEX"
\b0 \\" REL=\\"
\b Sibling
\b0 \\" 
\b TITLE
\b0 =\\"%
\b v
\b0 \\">\\n", rindex(tmp,'/')+1,[[
\b docInfoList
\b0  objectAt:(i-1)] 
\b docTitle]
\b0 );\
			\}\
		\}\
//	\}\
	\
	// 
\i continue for "peer" documents
\i0 \
	// 
\i for peers, we get a list of doc IDs that are space-separated
\i0 \
	i=0; 
\b peers
\b0  = [self peers];\
	
\b while
\b0  (peers[i] && (1 == 
\b sscanf
\b0 (peers+i,"
\b %x
\b0 ",&
\b aDocID
\b0 ))) \{\
		theDocInfo = [
\b docInfoTable
\b0  valueForKey:
\b docID
\b0 ];\
		if (
\b theDocInfo
\b0 ) \{\
			
\b strcpy
\b0 (tmp, [theDocInfo 
\b docPath
\b0 ]);\
			*rindex(tmp,'.')=0;\
			NXPrintf(s,"<
\b LINK HREF
\b0 =\\"../%
\b V.
\b0 "
\b\fc1\cf1 HTMD_EXT
\b0\fc0\cf0 "
\b /"HTML_INDEX"
\b0 \\" REL=\\"
\b Peer
\b0 \\" 
\b TITLE
\b0 =\\"%
\b v
\b0 \\">\\n", rindex(tmp,'/')+1,[theDocInfo 
\b docTitle]
\b0 );\
		\}\
		while(peers[i] && (peers[i] != ' ')) 
\b i++
\b0 ;  //
\i skip ahead
\i0 \
		while(peers[i] == ' ') 
\b i++
\b0 ;  //
\i skip ahead
\i0 \
	\}\
		\
	NXPrintf(s,"
\b </HEAD>
\b0 \\n");\
	return self;\
\}\
\
- 
\b writeHTMLTrailer
\b0 :(
\b NXStream
\b0  *)s	// 
\i optional; user-preference
\i0 \
\{\
	char tmp[MAXPATHLEN];\
	long aDocID;\
	id	 theDocInfo;\
	id   docInfoList;\
	NXAtom peers;\
	int	i;\
\
	// 
\i we should try to check the user's preferences here
\i0 \
	if (
\b ![self emitTrailer]
\b0 ) 
\b return
\b0  self;\
	\
	// 
\i write HR, then parent, peers, then author, date, then\

\i0 	NXPrintf(s,"\\n\\n
\b <!-- TRAILER -->
\b0 \\n<
\b HR
\b0 >\\n<
\b DL COMPACT
\b0 >\\n");\
\
	
\b sscanf
\b0 (tmp, "%x", &
\b aDocID
\b0 );\
	
\b theDocInfo
\b0  = [
\b docInfoTable
\b0  valueForKey:
\b aDocID
\b0 ];\
	if (
\b theDocInfo
\b0 ) \{\
		strcpy(
\b tmp
\b0 , [theDocInfo 
\b docPath
\b0 ]);\
		*rindex(tmp,'
\b .
\b0 ')=
\b 0
\b0 ;\
		NXPrintf(s,"\\n
\b <DT>Go Up (Parent):</DT>
\b0 \\n
\b <DD>[<A HREF
\b0 =\\"../%
\b V.
\b0 "
\b\fc1\cf1 HTMD_EXT
\b0\fc0\cf0 "
\b /"HTML_INDEX"
\b0 \\">%v<
\b /A
\b0 >
\b ]
\b0 \\n", 
\b rindex
\b0 (tmp,'
\b /
\b0 ')+1,[theDocInfo 
\b docTitle]
\b0 );\
	\}\
	\
	docInfoList = [[NXApp 
\b navigator
\b0 ] 
\b query
\b0 :[self 
\b docIDStr
\b0 ] 
\b field
\b0 :
\b PARENT
\b0 ];\
	i = [docInfoList count];\
	if(
\b i
\b0 ) NXPrintf(s,"\\n<
\b DT
\b0 >
\b For More Details (Children):
\b0 <
\b /DT
\b0 ><
\b DD
\b0 >\\n");;\
	for (;i;i--) \{\
		strcpy(
\b tmp
\b0 , [[
\b docInfoList
\b0  objectAt:(i-1)] 
\b docPath
\b0 ]);\
		*rindex(tmp,'.')=0;\
		NXPrintf(s,"\\n
\b [
\b0 <
\b A HREF
\b0 =\\"../%
\b V.
\b0 "
\b\fc1\cf1 HTMD_EXT
\b0\fc0\cf0 "
\b /"HTML_INDEX"
\b0 \\">%v<
\b /A
\b0 >
\b ]
\b0 \\n", rindex(tmp,'/')+1,[[
\b docInfoList
\b0  objectAt:(i-1)] 
\b docTitle]
\b0 );\
	\}\
	\
	// 
\i for peers, we get a list of doc IDs that are space-separated
\i0 \
	i=0; 
\b peers
\b0  = [self peers];\
	if(
\b strlen
\b0 (
\b peers
\b0 )) NXPrintf(s,"\\n<
\b DT
\b0 >
\b See Also (Peers):
\b0 <
\b /DT
\b0 ><
\b DD
\b0 >\\n");;\
	
\b while
\b0  (peers[i] && (1 == 
\b sscanf
\b0 (peers+i,"
\b %x
\b0 ",&
\b aDocID
\b0 ))) \{\
		theDocInfo = [
\b docInfoTable
\b0  valueForKey:
\b aDocID
\b0 ];\
		if (
\b theDocInfo
\b0 ) \{\
			
\b strcpy
\b0 (tmp, [theDocInfo 
\b docPath
\b0 ]);\
			*rindex(tmp,'.')=0;\
			NXPrintf(s,"\\n
\b [
\b0 <
\b A HREF
\b0 =\\"../%
\b V.
\b0 "
\b\fc1\cf1 HTMD_EXT
\b0\fc0\cf0 "
\b /"HTML_INDEX"
\b0 \\">%v<
\b /A
\b0 >
\b ]
\b0 \\n", 
\b rindex
\b0 (tmp,'
\b /
\b0 ')+1,[theDocInfo 
\b docTitle]
\b0 );\
		\}\
		while(peers[i] && (peers[i] != ' ')) 
\b i++
\b0 ;  //
\i skip ahead
\i0 \
		while(peers[i] == ' ') 
\b i++
\b0 ;  //
\i skip ahead
\i0 \
	\}\
\
//	if ([self 
\b parent
\b0 ] != 
\b NXUniqueString
\b0 (
\b ""
\b0 )) \{\
		docInfoList = [[NXApp 
\b navigator
\b0 ] 
\b query
\b0 :[self 
\b parent
\b0 ] 
\b field
\b0 :
\b PARENT
\b0 ];\
		i = [docInfoList count];\
		if(
\b i > 1
\b0 ) NXPrintf(s,"\\n<
\b DT
\b0 >
\b See Also (Siblings):
\b0 <
\b /DT
\b0 ><
\b DD
\b0 >\\n");;\
		for (;i;i--) \{\
			if ([
\b docInfoList
\b0  objectAt:(i-1)] != 
\b self
\b0 ) \{\
				strcpy(
\b tmp
\b0 , [[
\b docInfoList
\b0  objectAt:(i-1)] 
\b docPath
\b0 ]);\
				*rindex(tmp,'.')=0;\
				NXPrintf(s,"\\n
\b [
\b0 <
\b A HREF
\b0 =\\"../%
\b V.
\b0 "
\b\fc1\cf1 HTMD_EXT
\b0\fc0\cf0 "
\b /"HTML_INDEX "
\b0 \\">%v<
\b /A
\b0 >
\b ]
\b0 \\n", rindex(tmp,'/')+1,[[
\b docInfoList
\b0  objectAt:(i-1)] 
\b docTitle]
\b0 );\
			\}\
		\}\
//	\}\
	NXPrintf(s,"
\b </DL>
\b0 \\n");\
\
// 12/2 commented out per ernie's request && shortened\
	NXPrintf(s,"<
\b HR
\b0 ><
\b ADDRESS
\b0 ><
\b A HREF
\b0 =\\"
\b %V
\b0 \\">
\b %v
\b0 <
\b /A
\b0 > was converted on 
\b %v
\b0  by <
\b A HREF
\b0 =\\"
\b http://xent.w3.org/eTextEngine
\b0 \\">the <I>
\i eText Engine
\i0 </I>, version 5, release %
\b v
\b0 <
\b /A
\b0 ><
\b /ADDRESS
\b0 >\\n", 
\b DOCINFOFILE, 
\b0 [self 
\b docTitle
\b0 ], [NXApp 
\b date
\b0 ], [NXApp 
\b versionStr
\b0 ]);\
	return self;\
\}\
\
- 
\b writeLaTeXHeader
\b0 :(
\b NXStream
\b0  *)s\
\{	\
	NXPrintf(s, "\\n\\\\
\b author
\b0 \{%w\}\\n\\\\
\b title
\b0 \{%w\}\\n\\\\
\b maketitle
\b0 \\n",[self 
\b author
\b0 ], [self 
\b docTitle
\b0 ]);\
	return self;\
\}\
\

\i @end\
\

\i0 //пппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппппп\
//	
\b Draw-specific Extensions
\b0 \
//
\i \
@implementation eTDocInfo(eDraw)\
\
	
\i0 // 
\i eventually, indexing information for Draw type documents goes here.\
\
@end
}
